﻿using CliWrap;
using CliWrap.Builders;
using CommunityToolkit.Mvvm.Input;
using Notifications.Wpf;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Windows.Input;

namespace VideoTools.Commands
{
    public class MargeM3U8Commands : NotifyPropertyBase
    {
        private static volatile MargeM3U8Commands _instance;
        private static readonly object SyncRoot = new object();
        private List<string> batchList = new List<string>();
        private bool done = false;

        public static MargeM3U8Commands Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (SyncRoot)
                    {
                        if (_instance == null)
                        {
                            _instance = new MargeM3U8Commands();
                        }
                    }
                }

                return _instance;
            }
        }

        private MargeM3U8Commands() { }

        private string _maxSize = "0";

        public string MaxSize
        {
            get => _maxSize;
            set => SetProperty(ref _maxSize, value);
        }

        private string _m3u8File;

        public string M3u8File
        {
            get => _m3u8File;
            set => SetProperty(ref _m3u8File, value);
        }

        private string _margeName;

        public string MargeName
        {
            get => _margeName;
            set => SetProperty(ref _margeName, value);
        }
        private string _batchPath;

        public string BatchPath
        {
            get => _batchPath;
            set => SetProperty(ref _batchPath, value);
        }
        private string _batchCount;

        public string BatchCount
        {
            get => _batchCount;
            set => SetProperty(ref _batchCount, value);
        }
        private string _margeStatus;

        public string MargeStatus
        {
            get => _margeStatus;
            set => SetProperty(ref _margeStatus, value);
        }
        private string _batchMargeInfo;

        public string BatchMargeInfo
        {
            get => _batchMargeInfo;
            set => SetProperty(ref _batchMargeInfo, value);
        }

        private bool isBatch = false;
        private ICommand _margeCommand;
        private ICommand _batchMargeCommand;
        private ICommand _openM3u8DialogCommand;
        private ICommand _openBatchM3u8DialogCommand;

        public ICommand MargeCommand =>
            _margeCommand
            ?? (
                _margeCommand = new RelayCommand<object>(x =>
                {
                    isBatch = false;
                    done = false;
                    string path = M3u8File;
                    FileInfo f = new FileInfo(path);
                    if (!f.Exists)
                    {
                        MargeStatus = "请确认是否存在 " + path;
                        return;
                    }
                    if (MargeName.Trim().Length == 0)
                    {
                        MargeStatus = "输出文件不能为空";
                        return;
                    }
                    if (!MargeName.EndsWith(".mp4"))
                    {
                        MargeStatus = "输出文件必须是mp4";
                        return;
                    }
                    string outfile = Path.GetDirectoryName(path) + "\\" + MargeName;
                    MargeStatus = outfile;
                    FileInfo o = new FileInfo(outfile);
                    if (o.Exists)
                    {
                        var content = new NotificationContent()
                        {
                            Title = "输出文件已存在,本次操作取消",
                            Type = NotificationType.Warning,
                            Message = path,
                        };
                        AppManager.Instance.NotificationManager.Show(content);
                        return;
                    }
                    if (!string.IsNullOrEmpty(MaxSize))
                    {
                        try
                        {
                            int maxSize = int.Parse(MaxSize);
                            if (maxSize > 0)
                            {
                                prevWork(Path.GetDirectoryName(path));
                            }
                        }
                        catch (Exception ex) { }
                    }

                    //this.mainForm.ShowInfoDialog(f.Name);
                    Process process = new Process();
                    process.StartInfo.FileName = "ffmpeg";
                    //process.StartInfo.WorkingDirectory = path;
                    process.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
                    string param =
                        "-i \"" + path + "\" -vcodec copy -acodec copy \"" + outfile + "\"";
                    //将输出信息重定向
                    process.StartInfo.Arguments = param;
                    process.StartInfo.CreateNoWindow = true; //显示命令行窗口
                    //不使用操作系统使用的shell启动进程
                    process.StartInfo.UseShellExecute = false;
                    //将输出信息重定向
                    process.StartInfo.RedirectStandardInput = true;
                    process.StartInfo.RedirectStandardOutput = true;
                    process.StartInfo.RedirectStandardError = true;
                    process.EnableRaisingEvents = true;

                    process.Exited += new EventHandler(p_Exited);
                    process.OutputDataReceived += new DataReceivedEventHandler(
                        p_OutputDataReceived
                    );
                    process.ErrorDataReceived += new DataReceivedEventHandler(p_ErrorDataReceived);

                    process.Start();
                    //开始异步读取输出
                    process.BeginOutputReadLine();
                    process.BeginErrorReadLine();
                })
            );

        void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            //这里是正常的输出
        }

        void p_Exited(object sender, EventArgs e) { }

        class M3u8Work
        {
            public M3u8Work(
                string dirPath,
                int oldWidth,
                int oldHeight,
                int newWidth,
                int newHeight
            )
            {
                path = dirPath;
                this.oldWidth = oldWidth;
                this.oldHeight = oldHeight;
                this.newWidth = newWidth;
                this.newHeight = newHeight;
            }

            public string path { get; }
            public int oldWidth { get; }
            public int oldHeight { get; }

            public int newWidth { get; }
            public int newHeight { get; }
        }

        private async void prevWork(string path)
        {
            string ext = "1.ts";
            List<string> files = new List<string>();
            files.AddRange(
                Directory.EnumerateFileSystemEntries(path, ext, SearchOption.AllDirectories)
            );
            BatchMargeInfo = "文件夹文件数量" + files.Count;
            CancellationToken cancellationToken = default;
            List<M3u8Work> needResizeWorkList = new List<M3u8Work>();
            List<M3u8Work> taskList = new List<M3u8Work>();
            foreach (string filePath in files)
            {
                var stdOutBuffer = new StringBuilder();
                var stdErrBuffer = new StringBuilder();
                var arguments = new ArgumentsBuilder();
                arguments.Add("-v").Add("error");
                arguments.Add("-show_entries").Add("stream=width,height");
                arguments.Add("-of").Add("csv=p=0:s=x");
                arguments.Add(filePath);

                var result = await Cli.Wrap("ffprobe")
                    .WithArguments(arguments.Build())
                    .WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdOutBuffer))
                    .WithStandardErrorPipe(PipeTarget.ToStringBuilder(stdErrBuffer))
                    .WithValidation(CommandResultValidation.None)
                    .ExecuteAsync(cancellationToken);
                var stdOut = stdOutBuffer.ToString();
                var stdErr = stdErrBuffer.ToString();
                string[] infos = stdOut.Split('\r');
                infos = infos[0].Split("x");
                int width = int.Parse(infos[0]);
                int height = int.Parse(infos[1]);
                int maxSize = int.Parse(MaxSize);

                if (width > maxSize || height > maxSize)
                {
                    int nwidth = width;
                    int nheight = height;
                    bool istooWidth = Math.Max(width, height) == width ? true : false;
                    double scale = maxSize / (double)Math.Max(width, height);
                    if (istooWidth)
                    {
                        nwidth = maxSize;
                        nheight = (int)(height * scale);
                    }
                    else
                    {
                        nheight = maxSize;
                        nwidth = (int)(width * scale);
                    }
                    M3u8Work w = new M3u8Work(
                        Path.GetDirectoryName(filePath),
                        width,
                        height,
                        nwidth,
                        nheight
                    );
                    needResizeWorkList.Add(w);
                }
            }
            if (needResizeWorkList.Count == 0)
            {
                return;
            }
            //MessageBox.Show("待处理m3u8数量 " + needResizeWorkList.Count);
            ext = "*.ts";
            foreach (M3u8Work wk in needResizeWorkList)
            {
                IEnumerable<string> fileList = Directory.EnumerateFileSystemEntries(
                    wk.path,
                    ext,
                    SearchOption.AllDirectories
                );
                foreach (string ff in fileList)
                {
                    M3u8Work w = new M3u8Work(
                        ff,
                        wk.oldWidth,
                        wk.oldHeight,
                        wk.newWidth,
                        wk.newHeight
                    );
                    taskList.Add(w);
                }
            }
            int workerThreads = 8; // 假设你想要设置线程池的工作线程数为10
            int completionPortThreads = 8; // 完成端口线程数（用于I/O操作完成）
            ThreadPool.SetMaxThreads(workerThreads, completionPortThreads);

            Task[] tasks = new Task[taskList.Count];
            for (int i = 0; i < taskList.Count; i++)
            {
                M3u8Work work = taskList[i];
                tasks[i] = Task.Run(() => doScale(work));
            }
            Task.WaitAll(tasks);
            
        }

        private async void doScale(M3u8Work work)
        {
            string xname = work.path.Replace(".ts", "___.ts");
            string ss = "scale=\"" + work.newWidth + ":" + work.newHeight + "\"";
            var stdOutBuffer1 = new StringBuilder();
            var stdErrBuffer1 = new StringBuilder();
            string p =
                "-i "
                + work.path
                + " -vf scale="
                + work.newWidth
                + ":"
                + work.newHeight
                + " -c:v h264_nvenc -gpu 0 -acodec copy -y "
                + xname;
            var result1 = await Cli.Wrap("ffmpeg")
                .WithArguments(p)
                .WithStandardOutputPipe(PipeTarget.ToStringBuilder(stdOutBuffer1))
                .WithStandardErrorPipe(PipeTarget.ToStringBuilder(stdErrBuffer1))
                .WithValidation(CommandResultValidation.None)
                .ExecuteAsync(new CancellationToken());
            string stdOut1 = stdOutBuffer1.ToString();
            string stdErr1 = stdErrBuffer1.ToString();
            File.Delete(work.path);
            File.Move(xname, work.path);
        }

        void p_ErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            if (!done)
            {
                if (isBatch)
                {
                    BatchMargeInfo = "正在合并";
                }
                else
                {
                    MargeStatus = "正在合并";
                }
            }

            //这里得到的是错误信息
            string info = e.Data;
            //video:113956kB audio:24191kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 2.020919%
            if (
                info != null
                && info.Contains("video")
                && info.Contains("audio")
                && info.Contains("subtitle")
                && info.Contains("other")
                && info.Contains("streams")
                && info.Contains("global")
                && info.Contains("headers")
                && info.Contains("muxing")
                && info.Contains("overhead")
            )
            {
                done = true;
                if (isBatch)
                {
                    BatchMargeInfo = "完成合并";
                }
                else
                {
                    MargeStatus = "完成合并";
                }
            }
        }

        public ICommand BatchMargeCommand =>
            _batchMargeCommand
            ?? (
                _batchMargeCommand = new RelayCommand<object>(o =>
                {
                    isBatch = true;
                    if (!string.IsNullOrEmpty(MaxSize))
                    {
                        try
                        {
                            int maxSize = int.Parse(MaxSize);
                            if (maxSize > 0)
                            {
                                prevWork(BatchPath);
                            }
                        }
                        catch { }
                    }
                    foreach (string p in batchList)
                    {
                        string outFileName = BatchPath + "\\" + p.Replace(".m3u8", ".mp4");
                        string path = BatchPath + "\\" + p + "\\index.m3u8";
                        Process process = new Process();
                        process.StartInfo.FileName = "ffmpeg";
                        //process.StartInfo.WorkingDirectory = path;
                        process.StartInfo.WindowStyle = ProcessWindowStyle.Normal;
                        string param =
                            "-i \"" + path + "\" -vcodec copy -acodec copy \"" + outFileName + "\"";
                        //将输出信息重定向
                        process.StartInfo.Arguments = param;
                        process.StartInfo.CreateNoWindow = true; //显示命令行窗口
                        //不使用操作系统使用的shell启动进程
                        process.StartInfo.UseShellExecute = false;
                        //将输出信息重定向
                        process.StartInfo.RedirectStandardInput = true;
                        process.StartInfo.RedirectStandardOutput = true;
                        process.StartInfo.RedirectStandardError = true;
                        process.EnableRaisingEvents = true;

                        process.Exited += new EventHandler(p_Exited);
                        process.OutputDataReceived += new DataReceivedEventHandler(
                            p_OutputDataReceived
                        );
                        process.ErrorDataReceived += new DataReceivedEventHandler(
                            p_ErrorDataReceived
                        );

                        process.Start();
                        //开始异步读取输出
                        process.BeginOutputReadLine();
                        process.BeginErrorReadLine();
                    }
                })
            );

        public ICommand OpenM3u8DialogCommand =>
            _openM3u8DialogCommand
            ?? (
                _openM3u8DialogCommand = new RelayCommand<object>(o =>
                {
                    OpenFileDialog openFileDialog = new OpenFileDialog();
                    string filter = "m3u8文件(*.m3u8)|*.m3u8";

                    openFileDialog.Filter = filter;
                    openFileDialog.RestoreDirectory = true;
                    openFileDialog.FilterIndex = 1;
                    if (openFileDialog.ShowDialog() == DialogResult.OK)
                    {
                        string filePath = openFileDialog.FileName;
                        FileInfo fileInfo = new FileInfo(filePath);
                        string name = fileInfo.Name;
                        M3u8File = filePath;
                        name = name.Split(".")[0];
                        MargeName = name + ".mp4";
                    }
                    else
                    {
                        return;
                    }
                })
            );

        public ICommand OpenBatchM3u8DialogCommand =>
            _openBatchM3u8DialogCommand
            ?? (
                _openBatchM3u8DialogCommand = new RelayCommand<object>(o =>
                {
                    string allPath = null;
                    using (FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog())
                    {
                        folderBrowserDialog.Description = "请选择m3u8视频文件夹";
                        DialogResult dialogResult = folderBrowserDialog.ShowDialog();

                        if (dialogResult != DialogResult.OK)
                        {
                            return;
                        }
                        //最后面不包含\
                        allPath = folderBrowserDialog.SelectedPath;
                    }

                    if (allPath == null)
                    {
                        return;
                    }
                    int count = 0;
                    DirectoryInfo allDi = new DirectoryInfo(allPath);
                    foreach (DirectoryInfo di in allDi.GetDirectories())
                    {
                        string diName = di.Name;
                        string m3u8 = allPath + "\\" + diName + "\\index.m3u8";
                        if (File.Exists(m3u8))
                        {
                            count++;
                            batchList.Add(diName);
                        }
                    }
                    BatchPath = allPath;
                    BatchCount = count.ToString() + "个任务";
                })
            );
    }
}
